/*
 * ENUM Discoverer. ENUM Demonstration software, demonstrating the potential of ENUM.
 * 
 * Copyright (C) 2009 SIDN and ISOC.nl
 * 
 * This file is part of ENUM Discoverer.
 *
 * ENUM Discoverer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ENUM Discoverer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with ENUM Discoverer.  If not, see <http://www.gnu.org/licenses/>.
 */

package nl.sidn.enumdiscoverer;

import java.util.ArrayList;

import uk.nominet.DDDS.ENUM;
import uk.nominet.DDDS.Rule;
import uk.nominet.DDDS.ENUM.EnumRule;


import android.util.Log;

/**
 * Performs ENUM lookups on DNS servers
 * @author	Grant Patterson (grant.patterson@catalyst.net.nz)
 */
public class EnumClient {

	static final String TAG = LogConfig.getLogTag(EnumClient.class);
	// whether DEBUG level logging is enabled (whether globally, or explicitly for this log tag)
	static final boolean DEBUG = LogConfig.isDebug(TAG);
	// whether VERBOSE level logging is enabled
	static final boolean VERBOSE = LogConfig.VERBOSE;
	
	// The ENUM DNS suffix
	private volatile String mEnumSuffix;
	// The DNS server(s) to use for ENUM lookups
	private volatile String[] mServers;
	// The country code that will be used for non-E.164 numbers
	private volatile String mCountryCode;
	
	public EnumClient(String enumSuffix, String[] dnsServers, String countryCode) {
		mEnumSuffix = enumSuffix;
		mServers = dnsServers;
		mCountryCode = countryCode;
	}
	
	/**
	 * Look up the ENUM data for the phone number.
	 * If the number isn't in E.164 format, then it will be converted to E.164 format using
	 * the country code that has been set.
	 * precondition: number is not null
	 * @return A list of all ENUM records found for the number. List is empty if no rules were found
	 */
	public ArrayList<EnumRecord> doEnumLookup(String number) {
		
		ArrayList<EnumRecord> enumRecords = new ArrayList<EnumRecord>();
		
		// mEnumSuffix might be changed on another thread. Make sure the value we log is the
		// one we use
		String suffix = mEnumSuffix;
		
		// we need a number to be in E.164 format to do an ENUM lookup
		String e164Num = Utils.phoneNumbertoE164(number, mCountryCode);

		if (e164Num == null) {
			if (DEBUG) Log.d(TAG, "Unable to convert number to E.164: " + number);
			return enumRecords;
		} else {
			if (VERBOSE) Log.v(TAG, "Converting to E.164: " + number + " -> " + e164Num);
		}
		
		if (DEBUG) Log.d(TAG, "doing ENUM lookup for " + e164Num + " in " + suffix);
		
		// do the lookup
		ENUM enm = newENUM(suffix);
		Rule[] rules = enm.lookup(e164Num);
    	
		for (Rule rule : rules) {
			if (rule instanceof EnumRule) {
				parseEnumRule((EnumRule)rule, enumRecords);
			}
		}
    	
		return enumRecords;
	}
	
	public void setEnumSuffix(String suffix) {
		mEnumSuffix = suffix;
	}
	
	public void setDnsServers(String[] servers) {
		mServers = servers;
	}
	
	public void setCountryCode(String code) {
		mCountryCode = code;
	}
	
	private ENUM newENUM(String suffix) {
		
		if (mServers == null || mServers.length == 0) {
			return new ENUM(suffix);
		} else {
			return new ENUM(mServers, suffix);
		}
	}
	
	private void parseEnumRule(EnumRule rule, ArrayList<EnumRecord> enumRecords) {
		
		// split service-field into enumservices
		String[] services = rule.getService().toLowerCase().split("\\+");
		
		// validate: must be of form e2u+(>= 1 enumservice)
		if (services.length < 2) return;
		if (!services[0].equals("e2u")) return;
		
		int order = rule.getOrder();
		int preference = rule.getPriority();
		String result = rule.evaluate();
		
		for (int i = 1; i < services.length; i++) {
			
			if (VERBOSE) Log.v(TAG, services[i] + " -> " + result);
			
			EnumRecord record = new EnumRecord(order, preference, services[i], result);
			enumRecords.add(record);
		}
	}
}
